package xyz.perkd.process.impl;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontFactory;
import org.apache.pdfbox.text.PDFTextStripper;
import xyz.perkd.model.POLine;
import xyz.perkd.model.POTicket;
import xyz.perkd.process.IProcessor;
import xyz.perkd.util.DateTimeUtil;
import xyz.perkd.util.PdfUtil;
import xyz.perkd.util.PdfWatermarkProperties;
import xyz.perkd.util.WaterMaker;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Processor2 implements IProcessor {

    public IProcessor.ProcessListener stateUpdate;
    public String path;

    public boolean isDebug = true;

    private Map<String, Integer> xfConfigMap;
    ExecutorService executorService = Executors.newSingleThreadExecutor();

    public void setDebug(boolean debug) {
        isDebug = debug;
    }

    public void process(String path, IProcessor.ProcessListener stateUpdate) {
        this.stateUpdate = stateUpdate;
        this.path = path;
        executorService.submit(this::start);
    }

    private void notify(String message) {
        SwingUtilities.invokeLater(() -> {
            if (stateUpdate != null) {
                stateUpdate.onProcess(message, 0);
            }
        });
    }
    private void notifyWarn(String message) {
        SwingUtilities.invokeLater(() -> {
            if (stateUpdate != null) {
                stateUpdate.onProcess(message, 1);
            }
        });
    }

    private void notifyErr(String message) {
        SwingUtilities.invokeLater(() -> {
            if (stateUpdate != null) {
                stateUpdate.onProcess(message, 2);
            }
        });
    }
    private void loadConfig() {
        xfConfigMap = new HashMap<String, Integer>();
        String filePath = "./xfconfig.txt";
        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                String[] result = line.split("<->");
                if (result.length == 2) {
                    int delDays  = Integer.parseInt(result[1]);
                    if (delDays == 0) {

                    } else {
                        xfConfigMap.put(result[0], delDays);
                    }
                } else {

                }
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void start() {
        loadConfig();

        SwingUtilities.invokeLater(() -> {
            if (stateUpdate != null) {
                stateUpdate.onStart();
            }
        });
        File file = new File(path);
        String workDir = file.getParent();
        String outputPath = workDir + "/" + file.getName() + "_output";
        try {
            PDDocument document = Loader.loadPDF(file);

            File outputDir = new File(outputPath);
            if (!outputDir.exists()) {
                outputDir.mkdir();
            }
            String outputOriPath = outputPath +"/原始";
            File outputOriDir = new File(outputOriPath);
            if (!outputOriDir.exists()) {
                outputOriDir.mkdir();
            }
            String outputFixPath = outputPath +"/修改";
            File outputFixDir = new File(outputFixPath);
            if (!outputFixDir.exists()) {
                outputFixDir.mkdir();
            }
            for (int i = 0; i < document.getNumberOfPages(); i++) {
                notify("第" + (i + 1) + "页 开始处理>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                PDPage page = document.getPage(i);

                PDFStreamParser parser = new PDFStreamParser(page);
                List<Object> tokens = parser.parse();


                PDFTextStripper stripper = new PDFTextStripper();
                String t = stripper.getText(document);
                System.out.println(t);
                notify("第" + (i + 1) + "页 解析开始");
                POTicket ticket = PoParser.parse(tokens, document, page);
                notify("第" + (i + 1) + "页 解析成功");

                // 原件存档
                archive(page, i, ticket, outputOriDir);

                // 更新要发给工厂的内容
                if (ticket.revisedData == "") {
                    notifyErr("第" + (i + 1) + "页 解析警告, Revised Data 为空");
                }
                if (ticket.orderData == "") {
                    notifyErr("第" + (i + 1) + "页 解析警告, Order Data 为空");
                }
                // 移除带金额的notice
                if (ticket.notice.contains("$")) {
                    if (ticket.noticeToken instanceof COSString) {
                        String oldStr = ((COSString) ticket.noticeToken).getString();
                        notifyWarn("第" + (i + 1) + "页 修改含金额的notice前: " + oldStr);
                        String newStr = ParseDollar(oldStr);
                        newStr = newStr.replace("€", "");
                        ((COSString) ticket.noticeToken).setValue(newStr.getBytes());
                        notifyWarn("第" + (i + 1) + "页 修改含金额的notice后: " + newStr);
                    } else if (ticket.noticeToken instanceof COSArray) {
                        for (int iIndex = 0; iIndex < ((COSArray) ticket.noticeToken).size(); iIndex++) {
                            String oldStr = ((COSArray) ticket.noticeToken).getString(iIndex);
                            if (oldStr != null && oldStr.contains("$")) {
                                notifyWarn("第" + (i + 1) + "页 修改含金额的notice前: " + oldStr);
                                String newStr = ParseDollar(oldStr);
                                newStr = newStr.replace("€", "");
                                ((COSArray) ticket.noticeToken).setString(iIndex, newStr);
                                notifyWarn("第" + (i + 1) + "页 修改含金额的notice后: " + newStr);
                            }
                        }
                    }
                }

                int delDay = shouldOffsetXTDate(ticket);
                if (delDay != 0) {
                    // XFDate 偏移
                    for (POLine line : ticket.lines) {
                        if (line.xfToken instanceof COSString) {
                            String origin = line.xf;
                            String fixed = DateTimeUtil.offsetDate(line.xfReal, delDay);
                            origin = origin.replace(line.xfReal, fixed);
                            ((COSString) line.xfToken).setValue(origin.getBytes());
                            line.xfRealFile = origin;
                            notifyWarn("第" + (i + 1) + "页 XFDATE 更新 " + line.xf + "->" + origin);
                        } else if (line.xfToken instanceof COSArray) {
                            // 按照整句查找
                            boolean find = false;
                            for (int iIndex = 0; iIndex < ((COSArray) line.xfToken).size(); iIndex++) {
                                String str = ((COSArray) line.xfToken).getString(iIndex);
                                if (str != null && str.indexOf(line.xfReal) >= 0) {
                                    String origin = str;
                                    String fixed = DateTimeUtil.offsetDate(line.xfReal, delDay);
                                    origin = origin.replace(line.xfReal, fixed);
                                    ((COSArray) line.xfToken).setString(iIndex, origin);
                                    line.xfRealFile = origin;
                                    notifyWarn("第" + (i + 1) + "页 XFDATE 更新 " + str + "->" + origin);
                                    find = true;
                                    break;
                                }
                            }
                            if (!find) {
                                // 有可能是拼凑起来的
                                String totalStr = "";
                                String fixed = DateTimeUtil.offsetDate(line.xfReal, delDay);
                                List<Integer> lenList = new ArrayList<Integer>();
                                for (int iIndex = 0; iIndex < ((COSArray) line.xfToken).size(); iIndex++) {
                                    String str = ((COSArray) line.xfToken).getString(iIndex);
                                    if (str != null) {
                                        lenList.add(totalStr.length());
                                        totalStr += str;
                                    } else {
                                        lenList.add(totalStr.length());
                                    }
                                }
                                int realIndex =  totalStr.indexOf(line.xfReal);
                                String dealStrBef = line.xfReal;
                                String dealStrAft = fixed;
                                if (realIndex >= 0) {
                                    for (int j = 0; j < lenList.size(); j++) {
                                        if (((COSArray) line.xfToken).get(j) instanceof COSString) {
                                            if (lenList.get(j) ==realIndex) {
                                                for (int k = j; k < lenList.size(); k++) {
                                                    if (((COSArray) line.xfToken).get(k) instanceof COSString) {
                                                        String str = ((COSArray) line.xfToken).getString(k);
                                                        if (dealStrBef.indexOf(str) == 0) {
                                                            dealStrBef = dealStrBef.substring(str.length());
                                                            ((COSArray) line.xfToken).setString(k, dealStrAft.substring(0, str.length()));
                                                            dealStrAft = dealStrAft.substring(str.length());
                                                            if (dealStrBef.length() == 0) {
                                                                notifyWarn("第" + (i + 1) + "页 XFDATE 更新 " + line.xfReal + "->" + fixed);
                                                                break;
                                                            }
                                                        }
                                                    }
                                                }
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                } else {
                    notifyWarn("第" + (i + 1) + "页 XFDATE 无需更新");
                }

                // 更新 page
                PDStream updatedStream = new PDStream(document);
                OutputStream out = updatedStream.createOutputStream(COSName.FLATE_DECODE);
                ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
                tokenWriter.writeTokens(tokens);
                out.close();
                page.setContents(updatedStream);

                if (PdfUtil.addPictureWaterMarker(document, page)) {
                    notify("第" + (i + 1) + "页 水印添加成功");
                } else {
                    notifyErr("第" + (i + 1) + "页 水印添加失败");
                }

                factory(page, i, ticket, outputFixDir);

                notify("第" + (i + 1) + "页 完成处理<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
            }
            document.close();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            SwingUtilities.invokeLater(() -> {
                if (stateUpdate != null) {
                    stateUpdate.onFinish(outputPath);
                }
            });
        }
    }

    private String ParseDollar(String oldStr) {
        String newStr = oldStr;
        List<String> result = new ArrayList<>();
        String[] spaceList = oldStr.split("  ");
        for (String string : spaceList) {
            String[] f1 = string.split(";");
            for (String s : f1) {
                String[] f2 = s.split(",");
                for (int k = 0; k < f2.length; k++) {
                    result.add(f2[k]);
                }
            }
        }
        for (int i = 0; i < result.size(); i++) {
            String dStr =  result.get(i);
            if (dStr.contains("$")) {
                if (newStr.contains(";"+dStr)) {
                    newStr = newStr.replace(";"+dStr, "");
                } else if (newStr.contains(","+dStr)) {
                    newStr = newStr.replace(","+dStr, "");
                } else if (newStr.contains(dStr + ";")) {
                    newStr = newStr.replace(dStr+ ";", "");
                } else if (newStr.contains(dStr + ",")) {
                    newStr = newStr.replace(dStr+ ",", "");
                } else if (newStr.contains(dStr)){
                    newStr = newStr.replace(dStr, "");
                }
            }
        }
        return newStr;
    }
    private void archive(PDPage page, int i, POTicket ticket, File outputDir) {
        PDDocument outputPageDoc = new PDDocument();
        outputPageDoc.addPage(page);
        File archiveFile = new File(outputDir, getPageNameForArchive(i, ticket));
        try {
            outputPageDoc.save(archiveFile);
            outputPageDoc.close();
            notify("第" + (i + 1) + "页 归档成功 " + archiveFile.getPath());
        } catch (IOException e) {
            notify("第" + (i + 1) + "页 归档失败");
            e.printStackTrace();
        }
    }

    public void factory(PDPage page, int i, POTicket ticket, File outputDir) {
        PDDocument outputPageDoc = new PDDocument();
        outputPageDoc.addPage(page);
        File factoryFile = new File(outputDir, getPageNameForFactory(i, ticket));
        try {
            outputPageDoc.save(factoryFile);
            outputPageDoc.close ();
            notify("第" + (i + 1) + "页 工厂文件生成成功 " + factoryFile.getPath());
        } catch (IOException e) {
            notify("第" + (i + 1) + "页 工厂文件生成失败");
            e.printStackTrace();
        }
    }

    private String getPageNameForArchive(int idx, POTicket ticket) {
        String name = "PO#" + ticket.orderNo + ".pdf";
        if (isDebug) {
            name = (idx + 1 + "_") + name;
        }
        return name;
    }

    private String getPageNameForFactory(int idx, POTicket ticket) {
        // String name = "PO#" + ticket.orderNo + "-STYLE#" + ticket.style + "#XF" + ticket.lines.get(0).xf.replace("//", "").trim() + "-" + ticket.revisedData.replace("//", "").trim()+ ".pdf";
        String name = "PO#" + ticket.orderNo;
        if (ticket.style != null && !ticket.style.isEmpty()) {
            name += "-STYLE#" + ticket.style;
        }

//        System.out.println("xf:" + ticket.lines.get(0).xf.replace("/", "").trim());
        name += "-XF" + ticket.lines.get(0).xfRealFile.replace("/", "").trim();

        String revisedData = ticket.revisedData.replace("€", "").replace("/", "").trim();
        String orderData = ticket.orderData.replace("€", "").replace("/", "").trim();
        if (revisedData != null && !revisedData.isEmpty()) {
            name += "-" + revisedData;
        } else {
            name += "-" + orderData;
        }
        System.out.println("redt:" + ticket.revisedData.replace("€", "").replace("/", "").trim());
        System.out.println("oddt:" + ticket.revisedData.replace("€", "").replace("/", "").trim());

        name += ".pdf";

        if (isDebug) {
            name = (idx + 1 + "_") + name;
        }
        return name;
    }

    private int shouldOffsetXTDate(POTicket ticket) {
        String shipTo = String.join("", ticket.shipTo);
        Iterator<Map.Entry<String, Integer>> iterator = xfConfigMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            String key = entry.getKey();
            Integer value = entry.getValue();
            if (shipTo.contains(key)) {
                return value;
            }
        }
        return 0;
    }

    private boolean checkPdfUseFontInstalled(PDDocument document) {
//        try {
//            for (PDPage page : document.getPages()) {
//                PDFont font = PDFontFactory.createFont(page.getCOSObject());
//                if (!isFontInstalled(font.getName()))
//                    return false;
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

        return true;
    }

    public  boolean isFontInstalled(String fontName) {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] allFonts = ge.getAvailableFontFamilyNames();
        return Arrays.asList(allFonts).contains(fontName);
    }
}
